/******************************************************************************
 * Filename : uc_net.c
 * Created  : 2009-10-01 by dfer.li
 * Description -
 * 
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#include "uc_net.h"
#include "uc_string.h"
#include "uc_cfg.h"

char ucnet_if_cfg_key[UCNET_IF_CFG_KEY_MAX][UC_BUF_LEN] = {"BOOTPROTO", "IPADDR", "NETMASK", "GATEWAY", "PREFIX", "NETWORK", "ONBOOT"};
char ucnet_if_cfg_key_ubuntu[UCNET_IF_CFG_KEY_MAX][UC_BUF_LEN] = {"iface %s inet", "address", "netmask", "gateway", "", "", ""};

void
ucnet_ip_to_net(char *subnet, int buf_sz, char *ip, char *netmask)
{
	UC_U32 ip_num, netmask_num, subnet_num;

	inet_pton(AF_INET, ip, &ip_num);
	inet_pton(AF_INET, netmask, &netmask_num);
	subnet_num = ip_num & netmask_num;
	inet_ntop(AF_INET, &subnet_num, subnet, buf_sz);	
}

int
ucnet_ip_valid(char *str, char *dst)  
{  
    int part1,part2,part3,part4;
	char tail = 0;
	
	if(dst != NULL) {
		*dst = 0;
	}
	
    if(str == NULL) {
        return ERROR;
    }	
	
    if(sscanf(str, "%d.%d.%d.%d%c", &part1, &part2, &part3, &part4, &tail) != 4) {
        return ERROR;  
    }

	if(tail != 0 || part1 < 0 || part1 > 255
		|| part2 < 0 || part2 > 255
		|| part3 < 0 || part3 > 255
		|| part4 < 0 || part4 > 255
	) {
        return ERROR;  
	}

	if(dst != NULL) {
		sprintf(dst, "%d.%d.%d.%d", part1, part2, part3, part4);
	}

    return OK;
}  

//0-static 1-dhcp
int
ucnet_if_get_mode_by_file(char *if_name)
{
	char val[UC_BUF_LEN];
	int ret = -1;

	val[0] = 0;
	if(ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_BOOTPROTO, val, sizeof(val)) == OK) {
		if(ucstr_strcasestr(val, "dhcp") != NULL) {
			ret = 1;
		} else {
			ret = 0;
		}
	}

	return ret;
}

int 
ucnet_if_test_flag(char *if_name, int flag)
{
	int sock;
	struct ifreq ifr;
	int ret;

	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
		uc_err("create socket error.\n");
		return -1;
	}

	memset(&ifr, 0, sizeof(struct ifreq));
	strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
	ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
	close(sock);
	if (ret < 0) {
		//uc_err("ioctl error[%d].\n", errno);
		return -1;
	}

	return ifr.ifr_flags & flag;
}

int
ucnet_if_get_stat(char *if_name)
{
	int ret;

	ret = ucnet_if_test_flag(if_name, IFF_UP);
	if(ret < 0) {
		return ERROR;
	} else if (ret == 0) {
		return UC_IF_DOWN;
	} else {
		return UC_IF_UP;
	}
}

int
ucnet_if_get_net_addr(char *if_name, char *ipstr, char *netmask)
{
	struct ifreq ifr;
	SA_IN *pAddr = (SA_IN *)&(ifr.ifr_addr);
	int fd;

	*ipstr = '\0';
	*netmask = '\0';

	fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		uc_err("open socket error.\n");
		return ERROR;
	}
	memset(&ifr, 0, sizeof(struct ifreq));
	uc_strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));

	
	if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
		close(fd);
		return ERROR;
	}

	inet_ntop(AF_INET, &pAddr->sin_addr, ipstr, INET_ADDRSTRLEN);

	if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0) {
		close(fd);
		return ERROR;
	}

	inet_ntop(AF_INET, &pAddr->sin_addr, netmask, INET_ADDRSTRLEN);

	close(fd);
	return OK;
}

/* up_down
#define UC_IF_UP 
#define UC_IF_DOWN 
*/
int
ucnet_if_get(char *if_str, 
						int (*fun)(char *if_name, char *ip, char *mask, char *gateway, int up_down, int mode, void *p, void *q),
						void *p, 
						void *q)
{
	char ipstr[INET_ADDRSTRLEN];
	char netmask[INET_ADDRSTRLEN];
	char gateway[INET_ADDRSTRLEN];
	int up_down, mode;
	char if_name[IFNAMSIZ];
	FILE *fp;
	char buf[UC_BUF_LINE_LEN];
	char *pTmp;
	int cnt = 0;
	int if_str_len = strlen(if_str);

	if ((fp = fopen("/proc/net/dev", "r")) == NULL) {
		uc_err("open /proc/net/dev error.\n");
		return ERROR;
	}

	cnt = 0;
	while(fgets(buf, UC_BUF_LINE_LEN, fp) != NULL) {
		ucstr_trim(buf);
		pTmp = strstr(buf, if_str);
		if (pTmp == NULL || pTmp != buf) {
			continue;
		}
		
		if (*(pTmp + if_str_len) < '0' || *(pTmp + if_str_len) > '9') {
			continue;
		}
		
		memcpy(if_name, pTmp, if_str_len + 1);
		if_name[if_str_len + 1] = 0;
		//uc_p("get interface [%s] buf[%s]\n", if_name, buf);
		//uc_p("pTmp[%s]\n", pTmp);
		
		up_down = ucnet_if_get_stat(if_name);
		if (up_down != UC_IF_UP) {
			continue;
		}
		//uc_p("if_name[%s] up_down [%s]", if_name, up_down == UC_IF_UP ? "UP" : "DOWN");
		mode = ucnet_if_get_mode_by_file(if_name);
		//uc_p("boot proto [%s]", mode == UC_IF_DHCP ? "dhcp" : "static");

		if (ucnet_if_get_net_addr(if_name, ipstr, netmask) != OK) {
			if (ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_IPADDR, ipstr, INET_ADDRSTRLEN) != OK) {
				uc_strncpy(ipstr, UC_NULL_STR, sizeof(ipstr));
			}
			if (ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_NETMASK, netmask, INET_ADDRSTRLEN) != OK) {
				uc_strncpy(netmask, UC_NULL_STR, sizeof(netmask));
			}
		}
		
		if (ucnet_if_get_gateway(if_name, gateway) != OK) {
			if (ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_GATEWAY, gateway, INET_ADDRSTRLEN) != OK) {
				uc_strncpy(gateway, UC_NULL_STR, sizeof(gateway));
			}
		}

		if (fun(if_name, ipstr, netmask, gateway, up_down, mode, p, q) != OK) {
			break;
		}
		cnt++;
	}

	fclose(fp);
	return cnt;	
}

/* input: ifname, ip, mask (net order) */
int
ucnet_if_set_ip(char *ifname, UC_U32 ip, UC_U32 mask)
{
	int sock_fd;
	int brd = (ip & mask) | ~mask;
	struct ifreq ifr;
	SA_IN *p = (SA_IN *) &(ifr.ifr_addr);

	memset(&ifr, 0, sizeof (ifr));
	memcpy(ifr.ifr_name, ifname, strlen(ifname));

	p->sin_family = AF_INET;

	if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return ERROR;
	}

	/* ip addr */
	p->sin_addr.s_addr = ip;
	if (ioctl(sock_fd, SIOCSIFADDR, &ifr)) {
		perror("SIOCSIFADDR");
		close(sock_fd);
		return ERROR;
	}

	if (ip == 0) {
		close(sock_fd);
		return OK;
	}

	/* network mask */
	p->sin_addr.s_addr = mask;
	if (ioctl(sock_fd, SIOCSIFNETMASK, &ifr)) {
		perror("SIOCSIFNETMASK");
		close(sock_fd);
		return ERROR;
	}
	/* broadcast address */
	p->sin_addr.s_addr = brd;
	if (ioctl(sock_fd, SIOCSIFBRDADDR, &ifr)) {
		perror("SIOCSIFBRDADDR");
		close(sock_fd);
		return ERROR;
	}
	close(sock_fd);
	return OK;
}


int
ucnet_if_cto_to_ubt() 
{
	char buf[UC_BUF_PATH_LEN];
	char buf_value[UC_BUF_LEN];
	char buf_key[UC_BUF_LEN];
	char if_name[UC_BUF_LEN];
	char cfg_path[UC_BUF_PATH_LEN];
	char netmask[UC_BUF_LEN];
	int i = 0;
	int n = 0;

	uc_system_ret(buf, sizeof(buf), "uname -a | grep Ubuntu");
	if(strlen(buf) >= 6) {
		if(!ucfile_exist(UC_CFG_IF_PATH)) {
			uc_system("mkdir -p %s", UC_CFG_IF_PATH);
		}
		
		n = sprintf(buf, "%s\n", "auto lo");
		ucfile_write_pos(UC_CFG_IF_FILE_UBUNTU_BAK, 0, buf, n, 0, 1);
		
		n = sprintf(buf, "%s\n", "iface lo inet loopback");
		ucfile_append(UC_CFG_IF_FILE_UBUNTU_BAK, buf, n);

		for(i = 0; i < 6; i++) {
			snprintf(if_name, sizeof(if_name), "eth%d", i);
			uc_system_ret(buf, sizeof(buf), "ifconfig | grep ^%s | awk '{print $1}'", if_name);
			if(strlen(buf) < 4) {
				continue;
			}
			
			snprintf(cfg_path, sizeof(cfg_path), "%sifcfg-%s", UC_CFG_IF_PATH, if_name);

			n = sprintf(buf, "\nauto %s\n", if_name);
			ucfile_append(UC_CFG_IF_FILE_UBUNTU_BAK, buf, n);

			if(uccfg_get_by_key(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_BOOTPROTO], buf_value, sizeof(buf_value)) != OK) {
				uc_system_ret(buf, sizeof(buf), "ps aux | grep [d]hclient.%s", if_name);
				if(strlen(buf) > 12) {
					uc_strncpy(buf_value, "dhcp", sizeof(buf_value));
				} else {
					uc_strncpy(buf_value, "static", sizeof(buf_value));
				}
				uccfg_set_by_key_ext(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_BOOTPROTO], '=', buf_value);
			}
			snprintf(buf_key, sizeof(buf_key), ucnet_if_cfg_key_ubuntu[UCNET_IF_CFG_KEY_BOOTPROTO], if_name);
			n = sprintf(buf, "%s %s\n", buf_key, buf_value);
			ucfile_append(UC_CFG_IF_FILE_UBUNTU_BAK, buf, n);
			
			if(ucstr_strcasestr(buf_value, "dhcp")) {
				continue;
			}

			if(uccfg_get_by_key(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_IPADDR], buf_value, sizeof(buf_value)) != OK) {
				if (ucnet_if_get_net_addr(if_name, buf_value, netmask) == OK) {
					uccfg_set_by_key_ext(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_IPADDR], '=', buf_value);
					uccfg_set_by_key_ext(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_NETMASK], '=', netmask);
				}
			}
			if(strlen(buf_value) > 0) {
				n = sprintf(buf, "%s %s\n", ucnet_if_cfg_key_ubuntu[UCNET_IF_CFG_KEY_IPADDR], buf_value);
				ucfile_append(UC_CFG_IF_FILE_UBUNTU_BAK, buf, n);
			}

			if(uccfg_get_by_key(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_NETMASK], buf_value, sizeof(buf_value)) == OK) {
				n = sprintf(buf, "%s %s\n", ucnet_if_cfg_key_ubuntu[UCNET_IF_CFG_KEY_NETMASK], buf_value);
				ucfile_append(UC_CFG_IF_FILE_UBUNTU_BAK, buf, n);
			}

			if(uccfg_get_by_key(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_GATEWAY], buf_value, sizeof(buf_value)) != OK) {
				if (ucnet_if_get_gateway(if_name, buf_value) == OK) {
					uccfg_set_by_key_ext(cfg_path, ucnet_if_cfg_key[UCNET_IF_CFG_KEY_GATEWAY], '=', buf_value);
				}
			}
			if(strlen(buf_value) > 0) {
				n = sprintf(buf, "%s %s\n", ucnet_if_cfg_key_ubuntu[UCNET_IF_CFG_KEY_GATEWAY], buf_value);
				ucfile_append(UC_CFG_IF_FILE_UBUNTU_BAK, buf, n);
			}
		}

		uc_system("mv -f %s %s", UC_CFG_IF_FILE_UBUNTU_BAK, UC_CFG_IF_FILE_UBUNTU);
		
	}

	return OK;
}

int
ucnet_if_get_file(char *if_name, int item, char *value_buf, int value_size)
{
	char cfg_path[UC_BUF_PATH_LEN];
	int ret = 0;

	*value_buf = '\0';
	if(item >= UCNET_IF_CFG_KEY_MAX) {
		return ERROR;
	}

	if(!ucfile_exist(UC_CFG_IF_PATH)) {
		uc_system("mkdir -p %s", UC_CFG_IF_PATH);
	}

	if(!ucfile_exist(UC_CFG_IF_PATH_UBUNTU)) {
		uc_system("mkdir -p %s", UC_CFG_IF_PATH_UBUNTU);
	}

	snprintf(cfg_path, sizeof(cfg_path), "%sifcfg-%s", UC_CFG_IF_PATH, if_name);
	ret = uccfg_get_by_key(cfg_path, ucnet_if_cfg_key[item], value_buf, value_size);

	//ucnet_if_cto_to_ubt();

	return ret;
}

int
ucnet_if_set_file(char *if_name, int item, char *value) 
{
	char cfg_path[UC_BUF_PATH_LEN];
	int ret = 0;

	if(item >= UCNET_IF_CFG_KEY_MAX) {
		return ERROR;
	}

	if(!ucfile_exist(UC_CFG_IF_PATH)) {
		uc_system("mkdir -p %s", UC_CFG_IF_PATH);
	}

	if(!ucfile_exist(UC_CFG_IF_PATH_UBUNTU)) {
		uc_system("mkdir -p %s", UC_CFG_IF_PATH_UBUNTU);
	}

	snprintf(cfg_path, sizeof(cfg_path), "%sifcfg-%s", UC_CFG_IF_PATH, if_name);
	if(strlen(ucnet_if_cfg_key[item]) <= 0) {
		return OK;
	}

	if(value == NULL) {
		ret = uccfg_del_by_key(cfg_path, ucnet_if_cfg_key[item]);
	} else {
		ret = uccfg_set_by_key_ext(cfg_path, ucnet_if_cfg_key[item], '=', value);
	}

	ucnet_if_cto_to_ubt();

	return ret;
}

int
ucnet_if_process(char *if_name,int if_stat, int if_proto, char *if_addr, char *if_mask, char *if_gateway)
{
	char cmd[UC_BUF_LEN];
	//int if_run = 0;

	uc_info("ip process if[%s] stat[%d] proto[%d] add[%s] mask[%s] gatway[%s]\n", 
		if_name, if_stat, if_proto, if_addr, if_mask, if_gateway);
	
	if(if_proto == UC_IF_DHCP) {
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_BOOTPROTO, "dhcp");
		sprintf(cmd, "ifdown %s; ifup %s", if_name, if_name);
	} else {
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_BOOTPROTO, "static");
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_IPADDR, if_addr);
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_NETMASK, if_mask);
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_GATEWAY, if_gateway);
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_PREFIX, NULL);
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_NETWORK, NULL);
		sprintf(cmd, "ifconfig %s %s netmask %s", if_name, if_addr, if_mask);
	}
	
	if (if_stat == UC_IF_UP) {
/* 
		if_run = ucnet_if_test_flag(if_name, IFF_RUNNING);
		if (if_run < 0) {
			uc_err("test interface flag error.\n");
			return ERROR;
		}
*/

		uc_system(cmd);
		
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_ONBOOT, "yes");
	} else {
		uc_system("ifdown %s", if_name);
		ucnet_if_set_file(if_name, UCNET_IF_CFG_KEY_ONBOOT, "no");
	}

	return OK;
}

char *
ucnet_if_get_mac(const char *ifname, char *mac)
{
	int sock = 0;
	struct ifreq ifr;
	
	*mac = '\0';
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
		uc_err("get if error\n");
		return mac;
	}
	
	memset(&ifr, 0, sizeof(ifr));
	uc_strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	if (ioctl(sock, SIOCGIFHWADDR, &ifr, sizeof(ifr)) == 0) {
		sprintf(mac,"%02X:%02X:%02X:%02X:%02X:%02X", 
			(unsigned char)ifr.ifr_hwaddr.sa_data[0], 
			(unsigned char)ifr.ifr_hwaddr.sa_data[1], 
			(unsigned char)ifr.ifr_hwaddr.sa_data[2], 
			(unsigned char)ifr.ifr_hwaddr.sa_data[3], 
			(unsigned char)ifr.ifr_hwaddr.sa_data[4],
			(unsigned char)ifr.ifr_hwaddr.sa_data[5]);
	} else {
		uc_err("ctrl if error\n");
	}
	close(sock);

	return mac;
}

int
ucnet_if_get_by_file(char *if_name, char *bootproto, char *ipstr, char *netmask, char *gatway)
{
	char buf[UC_BUF_LEN];

	memset(buf, 0, UC_BUF_LEN);
	ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_BOOTPROTO, buf, UC_BUF_LEN - 1);
	uc_strncpy(bootproto, buf, UC_BUF_LEN);

	memset(buf, 0, UC_BUF_LEN);
	ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_IPADDR, buf, UC_BUF_LEN - 1);
	uc_strncpy(ipstr, buf, UC_BUF_LEN);

	memset(buf, 0, UC_BUF_LEN);
	ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_NETMASK, buf, UC_BUF_LEN - 1);
	uc_strncpy(netmask, buf, UC_BUF_LEN);

	memset(buf, 0, UC_BUF_LEN);
	ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_GATEWAY, buf, UC_BUF_LEN - 1);
	uc_strncpy(gatway, buf, UC_BUF_LEN);

	return OK;
}

int
ucnet_if_get_mask_by_file(char *if_name, char *netmask)
{
	char buf[UC_BUF_LEN];

	memset(buf, 0, UC_BUF_LEN);
	ucnet_if_get_file(if_name, UCNET_IF_CFG_KEY_NETMASK, buf, UC_BUF_LEN - 1);
	uc_strncpy(netmask, buf, UC_BUF_LEN);

	return OK;
}

int
ucnet_if_get_wlan(char *if_name)
{
	char cmd_buf_ret[UC_BUF_LEN];

	cmd_buf_ret[0] = 0;
	*if_name = 0;
	uc_system_ret(cmd_buf_ret, UC_BUF_LEN - 1, "ifconfig | grep ^%s* | awk '{print $1}'", UC_WIFI_IF_PREFIX);
	ucstr_trim(cmd_buf_ret);
	if (strlen(cmd_buf_ret) > 0 && strstr(cmd_buf_ret, UC_WIFI_IF_PREFIX) != NULL){
		sscanf(cmd_buf_ret, "%[a-z0-9]", if_name);
		return OK;
	} 

	return ERROR;
}

int
ucnet_if_get_type(char *if_name)
{
	char *p = NULL;
	char *netif_type_names[UCNET_IF_TYPE_MAX] = {"eth", "wlan", "ppp", "wwan", "usb"};
	int i = 0;

	for (i = 0; i < UCNET_IF_TYPE_MAX; i++) {
		p = strstr(if_name,netif_type_names[i]); 
		if (p == if_name) {
			return i;
		}
	}

	uc_err("get if type error, [%s]\n", if_name);

	return -1;
}

//eth0, return 0
int
ucnet_if_get_index(char *if_name)
{
	char *pTmp = NULL;
	int if_name_len = 0;
	int ret = -1;
	int flag = 0;
	
	if(if_name == NULL) {
		uc_err("get if index error, name null\n");
		return ERROR;
	}

	if_name_len = strlen(if_name);
	if(if_name_len <= 0) {
		uc_err("get if index error, name length 0\n");
		return ERROR;
	}

	pTmp = if_name + if_name_len - 1;
	while(pTmp != if_name) {
		if(*pTmp >= '0' && *pTmp <= '9') {
			flag = 1;
			pTmp--;
		} else {
			break;
		}
	}

	if(flag > 0) {
		if(*pTmp < '0' || *pTmp > '9') {
			pTmp++;
		}
		ret = atoi(pTmp);
	}

	if (ret < 0) {
		uc_err("get if index error, not found index\n");
	}

	return ret;
}

//eth0_1, return eth0 and 1
int
ucnet_if_get_subindex(char *if_subname, char *if_name)
{
	char *pTmp = NULL;
	int if_name_len = 0;
	int ret = -1;
	int flag = 0;
	
	if(if_name != NULL) {
		*if_name = 0;
	}
	if(if_subname == NULL) {
		uc_err("get if subindex error, name null\n");
		return ERROR;
	}

	if_name_len = strlen(if_subname);
	if(if_name_len <= 0) {
		uc_err("get if subindex error, name length 0\n");
		return ERROR;
	}

	pTmp = if_subname + if_name_len - 1;
	while(pTmp != if_subname) {
		if(*pTmp >= '0' && *pTmp <= '9') {
			flag = 1;
			pTmp--;
		} else {
			break;
		}
	}

	if(flag > 0) {
		if(*pTmp < '0' || *pTmp > '9') {
			pTmp++;
		}
		ret = atoi(pTmp);
		if(if_name != NULL && pTmp > if_subname) {
			pTmp--;
			memcpy(if_name, if_subname, pTmp - if_subname);
			if_name[pTmp - if_subname] = 0;
		}
	}

	if (ret < 0) {
		uc_err("get if subindex error, not found index\n");
	}

	return ret;
}


static int 
ucnet_if_gw_read_nl(int fd, char *buf, int seqNum, int pId)
{
	struct nlmsghdr *nlHdr = NULL;
	int readLen = 0, msgLen = 0;
	char *bufPtr = buf;

	do{
		if((readLen = recv(fd, bufPtr, UC_NL_BUFSIZE - msgLen, 0)) < 0){
			uc_err("readNL sock recv error\n");
			return -1;
		}
		
		nlHdr = (struct nlmsghdr *)bufPtr;

		if((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)) {
			uc_err("readNL recv head error\n");
			return -1;
		}

		if(nlHdr->nlmsg_type == NLMSG_DONE) {
			//printf("nlr done\n");
			break;
		}
		else{
			//printf("nlr read %d bytes\n", readLen);
			bufPtr += readLen;
			msgLen += readLen;
		}

		if((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) {
			/* return if its not */
			break;
		}
	} while((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId));
	return msgLen;
}

static int
ucnet_if_gw_parse_routes(struct nlmsghdr *nlHdr, char *netif, char *gateway)
{
	struct rtmsg *rtMsg = NULL;
	struct rtattr *rtAttr = NULL;
	int rtLen;
	u_int gateWay;
	char ifName[IF_NAMESIZE];
	int get_gateway = 0;

	rtMsg = (struct rtmsg *)NLMSG_DATA(nlHdr);
	if((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN)) {
		//uc_err("nl not af_inet or table main\n");
		return ERROR;
	}

	rtAttr = (struct rtattr *)RTM_RTA(rtMsg);
	rtLen = RTM_PAYLOAD(nlHdr);
	for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen)){
		switch(rtAttr->rta_type) {
			case RTA_OIF:
				if_indextoname(*(int *)RTA_DATA(rtAttr), ifName);
			break;
			case RTA_GATEWAY:
				gateWay = *(u_int *)RTA_DATA(rtAttr);
				get_gateway = 1;
			break;
		}
	 }

	if (get_gateway > 0 && strcmp(ifName, netif) == 0) {
		inet_ntop(AF_INET, &gateWay, gateway, INET_ADDRSTRLEN);
		return OK;
	}

	return ERROR;
}

int
ucnet_if_get_gateway(char *netif, char *gateway)
{
	char msg_buf[UC_NL_BUFSIZE]; 
	struct nlmsghdr *nlMsg;
	//struct rtmsg *rtMsg;
	int fd = 0;
	int len = 0;
	int msgSeq = 0;
	int if_get = 0;

	*gateway = 0;
	if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
		uc_err("nlr create socket error\n");
		return ERROR;
	}

	memset(msg_buf, 0, UC_NL_BUFSIZE);
	nlMsg = (struct nlmsghdr *)msg_buf;
	//rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg);
	nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));	
	nlMsg->nlmsg_type = RTM_GETROUTE;
	nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;	
	nlMsg->nlmsg_seq = msgSeq++; 
	nlMsg->nlmsg_pid = getpid();

	if (send(fd, nlMsg, nlMsg->nlmsg_len, 0) <= 0) {
		uc_err("nlr send message error\n");
		uc_close(fd);
		return ERROR;
	}

	len = ucnet_if_gw_read_nl(fd, msg_buf, msgSeq, nlMsg->nlmsg_pid);
	if (len < 0) {
		uc_close(fd);
		return ERROR;
	}
	
	for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len)){
		 if (ucnet_if_gw_parse_routes(nlMsg, netif, gateway) == OK) {
		 	if_get = 1;
			break;
		 }
	}	
	uc_close(fd);

	if (if_get > 0) {
		return OK;
	}

	return ERROR;	
}

int
ucnet_if_get_cnt(void)
{
	char cmd_buf_ret[UC_BUF_LEN];
	int if_cnt = 0;
	
	cmd_buf_ret[0] = 0;
	uc_system_ret(cmd_buf_ret, UC_BUF_LEN - 1, "lspci | grep Ethernet | wc -l");

	if (strlen(cmd_buf_ret) > 0) {
		if_cnt = atoi(cmd_buf_ret);
		return if_cnt;
	} else {
		return ERROR;
	}
}

int
ucnet_if_check_up_down(char *if_name, int *rx_flow, int *tx_flow)
{
	char cmd_buf_ret[UC_BUF_LEN];
	char rx_net_flow[UC_BUF_LEN];
	char tx_net_flow[UC_BUF_LEN];
	
	cmd_buf_ret[0] = 0;
	rx_net_flow[0] = 0;
	rx_net_flow[0] = 0;
	
	uc_system_ret(cmd_buf_ret, UC_BUF_LEN - 1, "ifconfig | grep ^%s* | awk '{print $1}'", if_name);
	ucstr_trim(cmd_buf_ret);

	if (strlen(cmd_buf_ret) > 0 && strstr(cmd_buf_ret, if_name) != NULL){
		uc_system_ret(rx_net_flow, UC_BUF_LEN - 1, "ifconfig %s | grep bytes | awk '{print $2}' | awk -F\":\" '{print $2}'", if_name);
		*rx_flow = atoi(rx_net_flow);
		
		uc_system_ret(tx_net_flow, UC_BUF_LEN - 1, "ifconfig %s | grep bytes | awk '{print $6}' | awk -F\":\" '{print $2}' ", if_name);
		*tx_flow = atoi(tx_net_flow);
		return OK;
	}
	
	return ERROR;
}

int
ucnet_rt_get(int (*fun)(char *dst, char *mask, char *gw, char *if_name, void *p, void *q), void *p, void *q)
{	
	char eth[IFNAMSIZ];
	char buf[UC_BUF_LINE_LEN];
	char dst[INET_ADDRSTRLEN], mask[INET_ADDRSTRLEN], gw[INET_ADDRSTRLEN];
	int flgs, ref, use, metric, mtu, win, ir;
	uint d, g, m;
	FILE *fp;
	int cnt;
		
	fp = fopen("/proc/net/route", "r");	
	if (!fp) {
		uc_err("open /proc/net/route error.\n");
		return ERROR;
	}

	/* skip first line */
	fgets(buf, UC_BUF_LINE_LEN, fp);

	cnt = 0;
	while (1) {
		if (fgets(buf, UC_BUF_LINE_LEN, fp) == NULL) {
			break;
		}
		if (sscanf(buf, "%s%x%x%X%d%d%d%x%d%d%d",
			   eth, &d, &g, &flgs, &ref, &use, &metric, &m, &mtu,
			   &win, &ir) != 11) {
			uc_err("Unsupported kernel route format\n");
			break;
		}
		
		/* down route */
		if (!(flgs & RTF_UP)) continue;

		/* 127.0.0.1 */
		if (d == htonl(0x7f000000)) continue;

		/* route with interface */
		if (g == 0) continue;

		inet_ntop(AF_INET, &d, dst, INET_ADDRSTRLEN);
		inet_ntop(AF_INET, &m, mask, INET_ADDRSTRLEN);
		inet_ntop(AF_INET, &g, gw, INET_ADDRSTRLEN);

		if (fun(dst, mask, gw, eth, p, q) != OK) { 
			break;
		}
		cnt++;
	} 
	fclose(fp);
	return cnt;
}

int
ucnet_rt_set(int command, char *dest, char *mask, char *gateway)
{
	char cmd_buf[UC_BUF_LEN_1K];
	int ret = OK;
	char net[UC_BUF_LEN_SMALL];

	ucnet_ip_to_net(net, sizeof(net), dest, mask);

	if (command == UCNET_ROUTE_ADD) {
		sprintf(cmd_buf, "ip route replace %s/%s via %s", net, mask, gateway);
	} else if (command == UCNET_ROUTE_DEL) {
		sprintf(cmd_buf, "ip route del %s/%s via %s", net, mask, gateway);
	} else {
		uc_err("rt set: bad command[%d]", command);
		return ERROR;
	}

	ret = uc_system(cmd_buf);
	uc_info("rt set: %s   ret[%d]\n", cmd_buf, ret);
	
	return ret;
}

static int 
ucnet_rt_save_one(char *dst, char *netmask, char *gw, char *if_name, void *p, void *q)
{
	char netif_ip[UC_BUF_LEN_SMALL], netif_netmask[UC_BUF_LEN_SMALL], netif_net[UC_BUF_LEN_SMALL];
	char tmp[UC_BUF_LEN_SMALL];
	char line_buf[UC_BUF_LEN_1K];
	int *pIdx = (int *)p;
	int n = 0;

	// skip link route && 169.254.0.0/255.255.0.0
	if (strcmp(dst, "169.245.0.0") == 0 && strcmp(netmask, "255.255.0.0") == 0) {
		return OK;
	}

	ucnet_if_get_net_addr(if_name, netif_ip, netif_netmask);
	ucnet_ip_to_net(netif_net, sizeof(netif_net), netif_ip, netif_netmask);
	ucnet_ip_to_net(tmp, sizeof(tmp), dst, netmask);
	if (strcmp(netif_net, tmp) == 0) {
		return OK;
	}

	// save to rt file and cmd file
	n = sprintf(line_buf, "%d %s %s %s %s\n", *pIdx, dst, netmask, gw, if_name);
	ucfile_append(UCNET_RT_FILE, line_buf, n);
	*pIdx += 1;

	n = sprintf(line_buf, "ip route replace %s/%s via %s dev %s\n", dst, netmask, gw, if_name);
	ucfile_append(UCNET_RT_CMD_FILE, line_buf, n);
	
	return OK;
}

void
ucnet_rt_save() 
{
	int idx = 0;

	uc_system("rm -f %s", UCNET_RT_FILE);
	uc_system("rm -f %s", UCNET_RT_CMD_FILE);
	ucnet_rt_get(ucnet_rt_save_one, &idx, NULL);
	uccfg_set_by_key_int(UCNET_RT_FILE, UCNET_RT_KEY, idx);
}

int 
ucnet_mask_to_num(char *netmask)
{
	int num = 0;
	UC_U32 ip = 0;
	int flag = 0;

	ip = ucsock_inet_aton(netmask);

	while(ip != 0) {
		if (ip & 0x1) {
			flag = 1;
			num++;
		} else {
			if (flag) {
				return -1;
			}
		}
		ip = ip >> 1;
	}
	
	return num;
}

char *
ucnet_num_to_mask(int num, char *dst)
{
	UC_U32 tmp = 0xffffffff;

	if(dst == NULL) {
		return NULL;	
	}
	
	*dst = 0;
	if(num > 32 || num < 0) {
		return NULL;
	}

	tmp = tmp >> (32 - num);
	tmp = tmp << (32 - num);

	ucsock_inet_ntoa_r(tmp, dst);
	
	return dst;
}

static char *
ucnet_sr_readline(char *text_buf, char *line_buf, int line_sz)
{
	char *p = text_buf;
	int n = 0;

	while (n < line_sz && *p != '\n' && *p != '\0') {
		line_buf[n++] = *p++;
	}

	if (n >= line_sz) {
		uc_debug("hs readline: line buf too small\n");
		return NULL;
	}

	line_buf[n] = '\0';

	if (*p == '\n') {
		p++;
	} else if (*p == '\0') {
		return NULL;
	}
	
	return p;	
}

int
ucnet_sr_route_add(int tid, char *net, char *netmask, char *via, char *dev)
{
	int ret = 0;
	char cmd[UC_BUF_LINE_LEN];

	if (dev == NULL) {
		uc_err("sr route add: bad argument\n");
		return ERROR;
	}
	
	if (via == NULL) {
		sprintf(cmd, "ip route add %s/%s dev %s table %d", net, netmask, dev, tid);
	} else {
		sprintf(cmd, "ip route add %s/%s via %s dev %s table %d", net, netmask, via, dev, tid);
	}
	
	ret = uc_system(cmd);
	if (ret != 0) {
		uc_err("sr route add error [%s]\n", cmd);
	}
	return ret;
}

int
ucnet_sr_route_check(int tid, char *net, char *netmask, char *via, char *dev)
{	
	char ret_buf[UC_BUF_LINE_LEN];
	char line_buf[UC_BUF_LEN];
	int ret = ERROR;
	char net_format[UC_BUF_LEN] = {0};
	char section_net[UC_BUF_LEN] = {0};
	char section_via[UC_BUF_LEN] = {0};
	char section_dev[UC_BUF_LEN] = {0};
	char *p = NULL;
	int route_ok = 0;
	int n = 0;
	
	ret = uc_system_ret(ret_buf, sizeof(ret_buf), "ip route list table %d", tid);
	if (ret != OK) {
		return ERROR;
	}

	if (strcmp(net, "0.0.0.0") == 0 && strcmp(netmask, "0.0.0.0") == 0) {
		sprintf(net_format, "default");
	} else {
		sprintf(net_format, "%s/%d", net, ucnet_mask_to_num(netmask));
	}

	p = ret_buf;
	while ((p = ucnet_sr_readline(p, line_buf, UC_BUF_LEN)) != NULL) {
		if (via == NULL) {
			n = sscanf(line_buf, "%s dev %s", section_net, section_dev);
			if (n == 2) {
				if (strcmp(net_format, section_net) == 0 && strcmp(dev, section_dev) == 0) {
					route_ok = 1;					
					break;
				}
			}
		} else {
			n = sscanf(line_buf, "%s via %s dev %s", section_net, section_via, section_dev);
			if (n == 3) {
				if (strcmp(net_format, section_net) == 0
					&& strcmp(via, section_via) == 0 && strcmp(dev, section_dev) == 0) {
					route_ok = 1;
					break;
				}
			}
		}
	}

	if (route_ok > 0) {
		return OK;
	}

	if (via != NULL) {
		uc_info("sr table %d check, lost route: (%s) via(%s) dev(%s)\n", tid, net_format, via, dev);
	} else {
		uc_info("sr table %d check, lost route: (%s) dev(%s)\n", tid, net_format, dev);
	}
	return ERROR;
}

int 
ucnet_sr_route_del(int tid)
{
	int ret = 0;
	char cmd[UC_BUF_LEN];

	sprintf(cmd, "ip route flush table %d 2>/dev/null", tid);	/* '2>/dev/null' means if occur error, don't print on screen */
	ret = uc_system(cmd);
	if (ret != 0) {
		uc_err("sr route del error, [%s]\n", cmd);
	}

	return ret;
}

void 
ucnet_sr_route_clean()
{
	int i = 0;
	
	for (i = UC_SR_TID_BASE; i < UC_SR_TID_MAX; i++) {
		ucnet_sr_route_del(i);
	}
}

int 
ucnet_sr_rule_add(int tid, char *src_ip)
{
	int ret = 0;
	char cmd[UC_BUF_LEN];

	sprintf(cmd, "ip rule add from %s lookup %d", src_ip, tid);
	ret = uc_system(cmd);
	if (ret != 0) {
		uc_err("sr rule add error, [%s]\n", cmd);
	}

	return ret;
}

int
ucnet_sr_rule_check(int tid, char *src_ip)
{
	int ret = 0;
	char ret_buf[UC_BUF_LINE_LEN];
	char line_buf[UC_BUF_LEN];
	char ip_section[UC_BUF_LEN];
	int id_section = 0;
	char *p = NULL;
	int n = 0;
	int tmp = 0;
	int rule_ok = 0;

	ret = uc_system_ret(ret_buf, sizeof(ret_buf), "ip rule show");
	if (ret != 0) {
		uc_err("sr rule check error, can't list rule\n");
		return ERROR;
	}

	p = ret_buf;
	while ((p = ucnet_sr_readline(p, line_buf, sizeof(line_buf))) != NULL) {
		n = sscanf(line_buf, "%d: from %s lookup %d", &tmp, ip_section, &id_section);
		if (n == 3) {
			if (id_section == tid && strcmp(ip_section, src_ip) == 0) {
				rule_ok = 1;
				break;
			}
		}
	}
	if (rule_ok > 0) {
		return OK;
	}

	uc_info("sr rule check, lost rule: ip=%s, tid=%d\n", src_ip, tid);
	return ERROR;
}

int
ucnet_sr_rule_del(int tid, char *src_ip)
{
	int ret = 0;
	char cmd[UC_BUF_LEN];

	if (src_ip != NULL) {
		sprintf(cmd, "ip rule del from %s lookup %d 2>/dev/null", src_ip, tid);
	} else {
		sprintf(cmd, "ip rule del lookup %d 2>/dev/null", tid);
	}

	ret = uc_system(cmd);

	return ret;
}

void 
ucnet_sr_rule_clean()
{
	int i = 0;
	for (i = UC_SR_TID_BASE; i < UC_SR_TID_MAX; i++) {
		while (ucnet_sr_rule_del(i, NULL) == 0) {
			// null 
		}
	}
}

static int 
ucnet_sr_get_dev_tid(char *dev)
{
	int index = 0;
	int netif_type = 0;
	int tid = -1;

	index = ucnet_if_get_index(dev);
	netif_type = ucnet_if_get_type(dev);

	if (index < 0 || netif_type < 0) {
		uc_err("sr get dev tid error, [%s]\n", dev);
		return -1;
	}
	
	if (netif_type == UCNET_IF_TYPE_ETH) {
		tid = UC_SR_TID_ETH_BASE + index;
		if (tid > UC_SR_TID_ETH_MAX) {
			uc_err("sr get tid exceed eth max, [%s]\n", dev);
			return -1;
		}
	} else if (netif_type == UCNET_IF_TYPE_WLAN) {
		tid = UC_SR_TID_WLAN_BASE;  // the one, the only
	} else if (netif_type == UCNET_IF_TYPE_PPP) {
		tid = UC_SR_TID_PPP_BASE + index;
		if (tid > UC_SR_TID_PPP_MAX) {
			uc_err("sr get tid exceed ppp max, [%s]\n", dev);
			return -1;
		}
	} else if (netif_type == UCNET_IF_TYPE_WWAN) {
		tid = UC_SR_TID_WWAN_BASE + index;
		if (tid > UC_SR_TID_WWAN_MAX) {
			uc_err("sr get tid exceed wwan max, [%s]\n", dev);
			return -1;
		}
	} else if (netif_type == UCNET_IF_TYPE_USB) {
		tid = UC_SR_TID_USB_BASE + index;
		if (tid > UC_SR_TID_USB_MAX) {
			uc_err("sr get tid exceed usb max, [%s]\n", dev);
			return -1;
		}
	}

	return tid;
}

int 
ucnet_sr_add_adv(char *dev, char *gateway)
{
	char ip[INET_ADDRSTRLEN];
	char netmask[INET_ADDRSTRLEN];
	char subnet[INET_ADDRSTRLEN];
	int netif_type = 0;
	int tid = 0;
	int ret = 0;

	netif_type = ucnet_if_get_type(dev);
	if (netif_type < 0) {
		uc_err("sr add adv get netif type error\n");
		return ERROR;
	}
	tid = ucnet_sr_get_dev_tid(dev);
	if (tid < 0) {
		uc_err("sr add adv get tid error\n");
		return ERROR;
	}	
	ucnet_if_get_net_addr(dev, ip, netmask);

	if (strlen(ip) < 7) {
		return OK;
	}
	
	if (netif_type == UCNET_IF_TYPE_PPP) {
		ret = ucnet_sr_route_add(tid, "0.0.0.0", "0.0.0.0", NULL, dev);
		if (ret != 0) {
			return ERROR;
		}
		ret = ucnet_sr_rule_add(tid, ip);
		if (ret != 0) {
			return ERROR;
		}
	} else {
		if (gateway != NULL && strlen(gateway) >= 7) {
			ret = ucnet_sr_route_add(tid, "0.0.0.0", "0.0.0.0", gateway, dev);	/* add default route */
			if (ret != 0) {
				return ERROR;
			}
		}
	
		ucnet_ip_to_net(subnet, sizeof(subnet), ip, netmask);
		ret = ucnet_sr_route_add(tid, subnet, netmask, NULL, dev);	/* add route for the subnet */
		if (ret != 0) {
			return ERROR;
		}
		ret = ucnet_sr_rule_add(tid, ip);	/* add a rule for the dev_ip */
		if (ret != 0) {
			return ERROR;
		}		
	}

	return OK;
}

int
ucnet_sr_check_adv(char *dev, char *gateway)
{
	char ip[INET_ADDRSTRLEN];
	char netmask[INET_ADDRSTRLEN];
	char subnet[INET_ADDRSTRLEN];
	int netif_type = 0;
	int tid = 0;
	int route_ok = 0;
	int route_gw_ok = 0;
	int rule_ok = 0;

	netif_type = ucnet_if_get_type(dev);
	if (netif_type < 0) {
		uc_err("sr add adv get netif type error\n");
		return ERROR;
	}
	tid = ucnet_sr_get_dev_tid(dev);
	if (tid < 0) {
		uc_err("sr add adv get tid error\n");
		return ERROR;
	}	
	ucnet_if_get_net_addr(dev, ip, netmask);

	if (strlen(ip) < 7) {
		return OK;
	}
	
	if (netif_type == UCNET_IF_TYPE_PPP) {
		route_gw_ok = ucnet_sr_route_check(tid, "0.0.0.0", "0.0.0.0", NULL, dev);
		rule_ok = ucnet_sr_rule_check(tid, ip);
		if (route_gw_ok != OK || rule_ok != OK) {
			if (route_gw_ok == OK) {
				ucnet_sr_route_del(tid);
			}
			if (rule_ok == OK) {
				while(ucnet_sr_rule_del(tid, NULL) == 0) {
					// null
				}
			}			
			return ERROR;
		}
	} else {
		ucnet_ip_to_net(subnet, sizeof(subnet), ip, netmask);

		if (gateway != NULL && strlen(gateway) >= 7) {/* has gateway, check the default route */
			route_gw_ok = ucnet_sr_route_check(tid, "0.0.0.0", "0.0.0.0", gateway, dev);
		} else {
			route_gw_ok = OK;
		}		
		route_ok = ucnet_sr_route_check(tid, subnet, netmask, NULL, dev);/* according subnet&netmask to check the route table */
		rule_ok = ucnet_sr_rule_check(tid, ip);/* according ip to check if rule exist */
		if (route_gw_ok != OK || route_ok != OK || rule_ok != OK) {
			if (route_gw_ok == OK || route_ok == OK) {
				ucnet_sr_route_del(tid);
			} 
			if (rule_ok == OK) {
				while(ucnet_sr_rule_del(tid, NULL) == 0) {
					// null
				}
			}
			return ERROR;
		}
	}

	return OK;
}

int 
ucnet_sr_del_adv(char *dev)
{
	int tid = 0;
	tid = ucnet_sr_get_dev_tid(dev);
	if (tid < 0) {
		uc_err("sr add adv get tid error\n");
		return ERROR;
	}	
	
	ucnet_sr_route_del(tid);
	while(ucnet_sr_rule_del(tid, NULL) == 0) {
		// null
	}

	return OK;
}

static void
ucnet_sr_cfg_clean()
{
	char *rt_table = "/etc/iproute2/rt_tables";
	char *rt_table_tmp = "/etc/iproute2/rt_tables.tmp";
	char *old_table_prefix = "sr";
	char line_buf[UC_BUF_LEN_1K];

	FILE *fp = NULL;
	FILE *fp_tmp = NULL;

	fp = fopen(rt_table, "r");
	if (fp == NULL) {
		uc_err("can't open file: %s\n", rt_table);
		return;
	}

	fp_tmp = fopen(rt_table_tmp, "w+");
	if (fp_tmp == NULL) {
		uc_err("can't open tmp file: %s\n", rt_table_tmp);
		return;
	}

	while(fgets(line_buf, sizeof(line_buf), fp) != NULL) {
		if ('0' <= line_buf[0] && line_buf[0] <= '9') {
			if (strstr(line_buf, old_table_prefix) == NULL) {
				fputs(line_buf, fp_tmp);
			}
		}
	}

	fclose(fp);
	fclose(fp_tmp);

	uc_system("mv %s %s", rt_table_tmp, rt_table);
}

void 
ucnet_sr_clean()
{
	ucnet_sr_cfg_clean();		/* clean old table prefix */
	ucnet_sr_route_clean();	/* flush route tables */
	ucnet_sr_rule_clean();	/* clean all the rules */
}

#ifdef __cplusplus
}
#endif
